package com.cancan.cloud.shop.utils;



import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.LinkedList;
import java.util.List;


/**
 * @program: StringUtils
 * @author: zzx
 * @date: Create in 10:07 2020/1/14
 * @description: 字符串工具类，包含常用操作以及校验
 * @version: 1.0.0
 */
public final class StringUtils {

    //String常用常量---------------------------------------------------------------------------------

    public static final int INDEX_NOT_FIND = -1;

    public static final String SPACE = " ";
    public static final String TAB = "	";
    public static final String DOT = ".";
    public static final String DOUBLE_DOT = "..";
    public static final String SLASH = "/";
    public static final String BACKSLASH = "\\";
    public static final String EMPTY = "";
    public static final String NULL = "null";
    public static final String CR = "\r";
    public static final String LF = "\n";
    public static final String CRLF = "\r\n";
    public static final String UNDERLINE = "_";
    public static final String DASHED = "-";
    public static final String COMMA = ",";
    public static final String DELIM_START = "{";
    public static final String DELIM_END = "}";
    public static final String BRACKET_START = "[";
    public static final String BRACKET_END = "]";
    public static final String COLON = ":";

    public static final String HTML_NBSP = "&nbsp;";
    public static final String HTML_AMP = "&amp;";
    public static final String HTML_QUOTE = "&quot;";
    public static final String HTML_APOS = "&apos;";
    public static final String HTML_LT = "&lt;";
    public static final String HTML_GT = "&gt;";

    public static final String EMPTY_JSON = "{}";
    public static final String UNDEFINED = "undefined";

    public static final List<String> UNDEFINED_LIST = new LinkedList<>();

    static {
        UNDEFINED_LIST.add(NULL);
        UNDEFINED_LIST.add(UNDEFINED);
    }

    //空字符串校验及操作-------------------------------------------------------------------------------------

    public static boolean isNotEmptyString(Object o) {
        return !isEmptyString(o);
    }

    /**
     * 字符串是否为空，空的定义如下:<br>
     * 1、为null <br>
     * 2、为""<br>
     *
     * @param str 被检测的字符串
     * @return 是否为空
     */
    public static boolean isEmpty(CharSequence str) {
        return str == null || str.length() == 0;
    }

    /**
     * 如果对象是字符串是否为空串空的定义如下:<br>
     * 1、为null <br>
     * 2、为""<br>
     *
     * @param obj 对象
     * @return 如果为字符串是否为空串
     */
    public static boolean isEmptyString(Object obj) {
        if (null == obj) {
            return true;
        } else if (obj instanceof String) {
            return 0 == ((String) obj).length();
        }
        return false;
    }

    /**
     * 字符串是否为非空白 空白的定义如下： <br>
     * 1、不为null <br>
     * 2、不为""<br>
     *
     * @param str 被检测的字符串
     * @return 是否为非空
     */
    public static boolean isNotEmpty(CharSequence str) {
        return !isEmpty(str);
    }

    /**
     * 当给定字符串为null时，转换为Empty
     *
     * @param str 被检查的字符串
     * @return 原字符串或者空串
     * @see #nullToEmpty(CharSequence)
     */
    public static String emptyIfNull(CharSequence str) {
        return nullToEmpty(str);
    }

    /**
     * 当给定字符串为null时，转换为Empty
     *
     * @param str 被转换的字符串
     * @return 转换后的字符串
     */
    public static String nullToEmpty(CharSequence str) {
        return nullToDefault(str, EMPTY);
    }

    /**
     * 如果字符串是<code>null</code>，则返回指定默认字符串，否则返回字符串本身。
     *
     * <pre>
     * nullToDefault(null, &quot;default&quot;)  = &quot;default&quot;
     * nullToDefault(&quot;&quot;, &quot;default&quot;)    = &quot;&quot;
     * nullToDefault(&quot;  &quot;, &quot;default&quot;)  = &quot;  &quot;
     * nullToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
     * </pre>
     *
     * @param str        要转换的字符串
     * @param defaultStr 默认字符串
     * @return 字符串本身或指定的默认字符串
     */
    public static String nullToDefault(CharSequence str, String defaultStr) {
        return (str == null) ? defaultStr : str.toString();
    }

    /**
     * 如果字符串是<code>null</code>或者&quot;&quot;，则返回指定默认字符串，否则返回字符串本身。
     *
     * <pre>
     * emptyToDefault(null, &quot;default&quot;)  = &quot;default&quot;
     * emptyToDefault(&quot;&quot;, &quot;default&quot;)    = &quot;default&quot;
     * emptyToDefault(&quot;  &quot;, &quot;default&quot;)  = &quot;  &quot;
     * emptyToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
     * </pre>
     *
     * @param str        要转换的字符串
     * @param defaultStr 默认字符串
     * @return 字符串本身或指定的默认字符串
     */
    public static String emptyToDefault(CharSequence str, String defaultStr) {
        return isEmpty(str) ? defaultStr : str.toString();
    }

    /**
     * 检查字符串是否为<code>null</code>、空白串、“null”、“undefined”
     *
     * @param str 被检查的字符串
     * @return 是否为null、空白串、“null”、“undefined”
     */
    public static boolean isEmptyOrUndefined(CharSequence str) {
        if (isEmpty(str)) {
            return true;
        }
        return isNullOrUndefinedStr(str);
    }

    /**
     * 是否为“null”、“undefined”，不做空指针检查
     *
     * @param str 字符串
     * @return 是否为“null”、“undefined”
     */
    private static boolean isNullOrUndefinedStr(CharSequence str) {
        String strString = str.toString().trim();
        return NULL.equals(strString) || "undefined".equals(strString);
    }

    /**
     * 是否包含空字符串
     *
     * @param strings 字符串列表
     * @return 是否包含空字符串
     */
    public static boolean hasEmpty(CharSequence... strings) {
        if (ArrayUtils.isEmpty(strings)) {
            return true;
        }
        for (CharSequence str : strings) {
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否包含空字符串
     *
     * @param strings 字符串列表
     * @return 是否包含空字符串
     */
    public static boolean hasEmpty(List<? extends CharSequence> strings) {
        if (ArrayUtils.isEmpty(strings)) {
            return true;
        }
        for (CharSequence str : strings) {
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 除去字符串头尾部的空白，如果字符串是<code>null</code>，依然返回<code>null</code>。
     *
     * <p>
     * 注意，和<code>String.trim</code>不同，此方法使用<code>NumberUtil.isBlankChar</code> 来判定空白， 因而可以除去英文字符集之外的其它空白，如中文空格。
     *
     * <pre>
     * trim(null)          = null
     * trim(&quot;&quot;)            = &quot;&quot;
     * trim(&quot;     &quot;)       = &quot;&quot;
     * trim(&quot;abc&quot;)         = &quot;abc&quot;
     * trim(&quot;    abc    &quot;) = &quot;abc&quot;
     * </pre>
     *
     * @param str 要处理的字符串
     * @return 除去头尾空白的字符串，如果原字串为<code>null</code>，则返回<code>null</code>
     */
    public static String trim(CharSequence str) {
        return trim(str, true, true);
    }


    /**
     * 除去字符串头尾部的空白符，如果字符串是<code>null</code>，依然返回<code>null</code>。
     *
     * @param str    要处理的字符串
     * @param front  是否除去前面空格
     * @param behind 是否除去厚面空格
     * @return 除去指定字符后的的字符串，如果原字串为<code>null</code>，则返回<code>null</code>
     */
    public static String trim(CharSequence str, boolean front, boolean behind) {
        if (str == null) {
            return null;
        }
        int length = str.length();
        int start = 0;
        int end = length;
        // 扫描字符串头部
        if (front) {
            while ((start < end) && (CharUtils.isBlankChar(str.charAt(start)))) {
                start++;
            }
        }
        // 扫描字符串尾部
        if (behind) {
            while ((start < end) && (CharUtils.isBlankChar(str.charAt(end - 1)))) {
                end--;
            }
        }

        if ((start > 0) || (end < length)) {
            return str.toString().substring(start, end);
        }

        return str.toString();
    }

    /**
     * 给定字符串数组全部做去首尾空格
     *
     * @param strings 字符串数组
     */
    public static void trim(String[] strings) {
        trim(strings, true, true);
    }

    /**
     * 给定字符串数组全部做去首尾空格
     *
     * @param strings 字符列表
     */
    public static void trim(List<String> strings) {
        trim(strings, true, true);
    }

    /**
     * 给定字符串数组全部做去首尾空格
     *
     * @param strings 字符串数组
     */
    public static void trim(String[] strings, boolean front, boolean behind) {
        if (null == strings) {
            return;
        }
        String str;
        for (int i = 0; i < strings.length; i++) {
            str = strings[i];
            if (null != str) {
                strings[i] = trim(str, front, behind);
                ;
            }
        }
    }

    /**
     * 给定字符串数组全部做去首尾空格
     *
     * @param strings 字符列表
     */
    public static void trim(List<String> strings, boolean front, boolean behind) {
        if (null == strings) {
            return;
        }
        for (int i = 0; i < strings.size(); i++) {
            String str = strings.get(i);
            strings.set(i, trim(str, front, behind));
        }
    }

    /**
     * 驼峰命名转下划线，返回为小写的下划线变量
     *
     * @param param 需要转换的驼峰字符串
     * @return 返回驼峰字符串
     */
    public static String camelToUnderline(String param) {
        return camelToUnderline(param, false);
    }

    /**
     * 驼峰命名转下划线，支持设置是否全部转换为大写
     *
     * @param param     需要转换的驼峰字符串
     * @param upperCase 是否转换为大写
     * @return 返回驼峰字符串
     */
    public static String camelToUnderline(String param, boolean upperCase) {
        if (isEmpty(param)) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(UNDERLINE);
            }
            if (upperCase) {
                //统一都转大写
                sb.append(Character.toUpperCase(c));
            } else {
                //统一都转小写
                sb.append(Character.toLowerCase(c));
            }


        }
        return sb.toString();
    }

    /**
     * 下划线转驼峰，返回驼峰格式的字符串
     *
     * @param param 下划线字符串
     * @return 驼峰字符串
     */
    public static String underlineToCamel(String param) {
        if (isEmpty(param)) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        // "_" 后转大写标志,默认字符前面没有"_"
        boolean flag = false;
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            //标志设置为true,跳过
            if (c == CharUtils.UNDERLINE) {
                flag = true;
            } else {
                if (flag) {
                    //表示当前字符前面是"_" ,当前字符转大写
                    sb.append(Character.toUpperCase(param.charAt(i)));
                    //重置标识
                    flag = false;
                } else {
                    sb.append(Character.toLowerCase(param.charAt(i)));
                }
            }
        }
        return sb.toString();
    }

    /**
     * 把string array or list用给定的符号symbol连接成一个字符串,支持前缀和后缀
     *
     * @param obj    需要处理的列表
     * @param symbol 链接的符号
     * @param pre    前缀
     * @param suf    后缀
     * @return 处理后的字符串
     */
    public static String joinString(Object obj, String symbol, String pre, String suf) {
        if (symbol == null) {
            symbol = StringUtils.EMPTY;
        }
        StringBuilder result = new StringBuilder();
        if (isNotEmpty(pre)) {
            result.append(pre);
        }
        if (ArrayUtils.isList(obj)) {
            for (Object o : (List) obj) {
                String temp = o.toString();
                if (temp.length() > 0) {
                    result.append(temp).append(symbol);
                }
            }
        } else if (ArrayUtils.isArray(obj) && obj instanceof Object[]) {
            Object[] objects = (Object[]) obj;
            for (Object temp : objects) {
                if (temp.toString().length() > 0) {
                    result.append(temp).append(symbol);
                }
            }
        } else if (ArrayUtils.isArray(obj)) {
            //这个是反射,性能较差,
            for (int i = 0; i < Array.getLength(obj); i++) {
                if (Array.get(obj, i) == null) {
                    continue;
                }
                String temp = Array.get(obj, i).toString();
                if (temp.trim().length() > 0) {
                    result.append(temp).append(symbol);
                }
            }
        }
        result.delete(result.length() - symbol.length(), result.length());
        if (isNotEmpty(suf)) {
            result.append(suf);
        }
        return result.toString();
    }

    /**
     * 字符串拆分，返回字符串数组 可以清除前缀和后缀
     *
     * @param str    需要拆分的字符串
     * @param symbol 拆分的分隔符
     * @param pre    前缀
     * @param suf    后缀
     * @return 返回字符串数组
     */
    public static String[] split(CharSequence str, String symbol, String pre, String suf) {
        if (null == str) {
            return new String[]{};
        }
        String strWithoutPreSuf = str.toString();
        if (isNotEmpty(pre) && strWithoutPreSuf.length() > pre.length() && strWithoutPreSuf.substring(0, pre.length()).equals(pre)) {
            strWithoutPreSuf = strWithoutPreSuf.substring(pre.length());
        }
        if (isNotEmpty(suf) && strWithoutPreSuf.length() > suf.length() && strWithoutPreSuf.substring(strWithoutPreSuf.length() - suf.length()).equals(suf)) {
            strWithoutPreSuf = strWithoutPreSuf.substring(0, (strWithoutPreSuf.length() - suf.length()));
        }
        return strWithoutPreSuf.split(symbol);
    }

    /**
     * 首字母大小写转换，设置为true为大写，设置为false为小写
     *
     * @param str   输入字符串
     * @param upper 是否转换为大写
     * @return 返回转换后的字符串
     */
    public static String caseFirstChar(String str, boolean upper) {
        if (upper) {
            return str.substring(0, 1).toUpperCase() + str.substring(1);
        } else {
            return str.substring(0, 1).toLowerCase() + str.substring(1);
        }
    }

    /**
     * {@link CharSequence} 转为字符串，<code>null</code>安全
     *
     * @param cs {@link CharSequence}
     * @return 字符串
     */
    public static String str(CharSequence cs) {
        return null == cs ? null : cs.toString();
    }

    /**
     * 去除字符串中指定的多个字符，如有多个则全部去除
     *
     * @param str   字符串
     * @param chars 字符列表
     * @return 去除后的字符
     */
    public static String removeAll(CharSequence str, char... chars) {
        if (null == str || ArrayUtils.isEmpty(chars)) {
            return str(str);
        }
        final int len = str.length();
        if (0 == len) {
            return str(str);
        }
        final StringBuilder builder = new StringBuilder(len);
        char c;
        for (int i = 0; i < len; i++) {
            c = str.charAt(i);
            if (!ArrayUtils.contains(chars, c)) {
                builder.append(c);
            }
        }
        return builder.toString();
    }

    /**
     * 只保留字符串中指定的多个字符，如有多个则全部去除
     *
     * @param str   字符串
     * @param chars 字符列表
     * @return 只保留的字符串
     */
    public static String keepAll(CharSequence str, char... chars) {
        if (null == str || ArrayUtils.isEmpty(chars)) {
            return null;
        }
        final int len = str.length();
        if (0 == len) {
            return str(str);
        }
        final StringBuilder builder = new StringBuilder(len);
        char c;
        for (int i = 0; i < len; i++) {
            c = str.charAt(i);
            if (ArrayUtils.contains(chars, c)) {
                builder.append(c);
            }
        }
        return builder.toString();
    }

    /**
     * 清理空白字符
     *
     * @param str 被清理的字符串
     * @return 清理后的字符串
     */
    public static String cleanBlank(CharSequence str) {
        if (str == null) {
            return null;
        }

        int len = str.length();
        final StringBuilder sb = new StringBuilder(len);
        char c;
        for (int i = 0; i < len; i++) {
            c = str.charAt(i);
            if (!CharUtils.isBlankChar(c)) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 优化subString：前面数起为自然数，后面数起为负数<br>
     * index从0开始计算，最后一个字符为-1<br>
     * 如果from和to位置一样，返回 "" <br>
     * 如果from或to为负数，则按照length从后向前数位置，如果绝对值大于字符串长度，则from归到0，to归到length<br>
     * 如果经过修正的index中from大于to，则互换from和to example: <br>
     * abcdefgh 0 0 =》 abcdefgh <br>
     * abcdefgh 0 -1 =》 abcdefg <br>
     * abcdefgh -1 0 =》 h <br>
     * abcdefgh 2 3 =》 c <br>
     * abcdefgh 2 -3 =》 cde <br>
     *
     * @param str       String
     * @param fromIndex 开始的index（包括）
     * @param toIndex   结束的index（不包括）
     * @return 字串
     */
    public static String sub(CharSequence str, int fromIndex, int toIndex) {
        if (isEmpty(str)) {
            return str(str);
        }
        int len = str.length();
        if (fromIndex < 0) {
            fromIndex = len + fromIndex;
            if (fromIndex < 0) {
                fromIndex = 0;
            }
        } else if (fromIndex > len) {
            fromIndex = len;
        }
        if (toIndex <= 0) {
            toIndex = len + toIndex;
            if (toIndex < 0) {
                toIndex = len;
            }
        } else if (toIndex > len) {
            toIndex = len;
        }
        if (toIndex < fromIndex) {
            int tmp = fromIndex;
            fromIndex = toIndex;
            toIndex = tmp;
        }
        if (fromIndex == toIndex) {
            return EMPTY;
        }

        return str.toString().substring(fromIndex, toIndex);
    }

    /**
     * 比较两个字符串（大小写不敏感）。
     *
     * <pre>
     * equalsIgnoreCase(null, null)   = true
     * equalsIgnoreCase(null, &quot;abc&quot;)  = false
     * equalsIgnoreCase(&quot;abc&quot;, null)  = false
     * equalsIgnoreCase(&quot;abc&quot;, &quot;abc&quot;) = true
     * equalsIgnoreCase(&quot;abc&quot;, &quot;ABC&quot;) = true
     * </pre>
     *
     * @param str1 要比较的字符串1
     * @param str2 要比较的字符串2
     * @return 如果两个字符串相同，或者都是<code>null</code>，则返回<code>true</code>
     */
    public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
        return equals(str1, str2, true);
    }

    /**
     * 比较两个字符串是否相等。
     *
     * @param str1       要比较的字符串1
     * @param str2       要比较的字符串2
     * @param ignoreCase 是否忽略大小写
     * @return 如果两个字符串相同，或者都是<code>null</code>，则返回<code>true</code>
     */
    public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
        if (null == str1) {
            // 只有两个都为null才判断相等
            return str2 == null;
        }
        if (null == str2) {
            // 字符串2空，字符串1非空，直接false
            return false;
        }

        if (ignoreCase) {
            return str1.toString().equalsIgnoreCase(str2.toString());
        } else {
            return str1.equals(str2);
        }
    }

    /**
     * 格式化字符串<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数,必须紧跟，不然报错<br>
     * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例：<br>
     * 通常使用：format("this is {} for {} ", "a", "b") =》 this is a for b<br>
     * 转义{}： format("this is \\{}  for {} and {}", "a", "b") =》 this is {} for a and b<br>
     * 转义\： format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
     *
     * @param strPattern 字符串模板
     * @param argArray   参数列表
     * @return 结果
     */
    public static String format(final CharSequence strPattern, final Object... argArray) {
        if (isEmpty(strPattern)) {
            return null;
        }
        char[] strChar = strPattern.toString().toCharArray();
        StringBuilder stringBuilder = new StringBuilder(strPattern.length() + 16);
        boolean hasHead = false, hasTransfer = false;
        for (int i = 0, j = 0; i < strChar.length; ) {
            //获取第一个非空填充字符串
            String stringFill;
            if (j < argArray.length &&
                    argArray[j] == null) {
                j++;
                continue;
            }
            if (j >= argArray.length) {
                stringFill = EMPTY;
            } else {
                stringFill = argArray[j].toString();
            }
            //当存在左花括号时，紧接右花括号则替换，无右花括号则报错->不再报错，则输出原来字符+左花括号
            if (hasHead) {
                if (CharUtils.DELIM_END == strChar[i]) {
                    stringBuilder.append(stringFill);
                    hasHead = false;
                    i++;
                    j++;
                    continue;
                } else {
//                    throw new IllegalArgumentException("模板格式出错：存在非转义的“{”后无紧跟“}”");
                    hasHead = false;
                    stringBuilder.append(CharUtils.DELIM_START).append(strChar[i]);
                    i++;
                    continue;
                }
            }
            // 没转义只识别“{”,“\”识别成转义，已经转义“{”=>“{” “\” =>“\",
            if (!hasTransfer) {
                if (CharUtils.DELIM_START == strChar[i]) {
                    hasHead = true;
                    i++;
                    continue;
                }
                if (CharUtils.BACKSLASH == strChar[i]) {
                    hasTransfer = true;
                    i++;
                    continue;
                }
            } else {
                switch (strChar[i]) {
                    case CharUtils.DELIM_START:
                        hasTransfer = false;
                        stringBuilder.append(CharUtils.DELIM_START);
                        i++;
                        continue;
                    case CharUtils.BACKSLASH:
                        hasTransfer = false;
                        stringBuilder.append(CharUtils.BACKSLASH);
                        i++;
                        continue;
                    default:
                        stringBuilder.append(CharUtils.BACKSLASH);
                }
            }
            stringBuilder.append(strChar[i]);
            i++;
        }
        return stringBuilder.toString();
    }


    /**
     * 替换字符串中的指定字符串
     *
     * @param str         字符串
     * @param fromIndex   开始位置（包括）
     * @param toIndex     结束位置，不包括位置
     * @param searchStr   被查找的字符串
     * @param replacement 被替换的字符串
     * @param ignoreCase  是否忽略大小写
     * @return 替换后的字符串
     */
    public static String replace(CharSequence str, int fromIndex, int toIndex, CharSequence searchStr, CharSequence replacement, boolean ignoreCase) {
        //需要改
        if (isEmpty(str) || isEmpty(searchStr)) {
            return str(str);
        }
        if (null == replacement) {
            replacement = EMPTY;
        }

        final int strLength = str.length();
        final int searchStrLength = searchStr.length();
        if (fromIndex > strLength) {
            return str(str);
        } else if (fromIndex < 0) {
            fromIndex = 0;
        }

        final StringBuilder result = new StringBuilder(strLength + 16);
        if (0 != fromIndex) {
            result.append(str.subSequence(0, fromIndex));
        }

        int preIndex = fromIndex;
        int index;
        while ((index = indexOf(str, searchStr, preIndex, toIndex, ignoreCase)) > -1) {
            result.append(str.subSequence(preIndex, index));
            result.append(replacement);
            preIndex = index + searchStrLength;
        }

        if (preIndex < strLength) {
            // 结尾部分
            result.append(str.subSequence(preIndex, strLength));
        }
        return result.toString();
    }

    /**
     * 查找字符串，返回第一个找到的第一个索引<br>
     *
     * @param str       字符串
     * @param searchStr 需要查找位置的字符串
     * @return 位置
     */
    public static int indexOf(CharSequence str, CharSequence searchStr) {
        return indexOf(str, searchStr, 0, str.length(), false);
    }

    /**
     * 指定范围内查找字符串，返回第一个找到的第一个索引<br>
     * 1.截断，记录阶段开始是的序列，2，判断长度是否前者更长，3，比较
     *
     * @param str        字符串
     * @param searchStr  需要查找位置的字符串
     * @param fromIndex  起始位置，支持负数（包括）
     * @param toIndex    结束位置，支持负数（不包括）
     * @param ignoreCase 是否忽略大小写
     * @return 位置
     */
    public static int indexOf(CharSequence str, CharSequence searchStr, int fromIndex, int toIndex, boolean ignoreCase) {
        if (isEmpty(str) || isEmpty(searchStr)) {
            return -1;
        }
        String stringScope = sub(str, fromIndex, toIndex);
        int endIndex = toIndex <= 0 ? (Math.max(str.length() + toIndex, 0)) : toIndex;
        int beforeIndex = fromIndex < 0 ? (Math.max(str.length() + fromIndex, 0)) : fromIndex;
        if (stringScope.length() < searchStr.length()) {
            return -1;
        }
        if (!ignoreCase) {
            // 不忽略大小写调用JDK方法
            int i = str.toString().indexOf(searchStr.toString(), beforeIndex);
            if (i < endIndex) {
                return i;
            }
            return -1;

        }

        for (int i = 0; i < stringScope.length() - searchStr.length(); i++) {
            if (isSubEquals(stringScope, i, searchStr, 0, searchStr.length(), true)) {
                return i + beforeIndex;
            }
        }
        return -1;
    }

    /**
     * 指定范围内查找字符串<br>
     *
     *
     * @param str 字符串
     * @param searchStr 需要查找位置的字符串
     * @param fromIndex 起始位置，支持负数（包括）
     * @param toIndex 结束位置，支持负数（不包括）
     * @param ignoreCase 是否忽略大小写
     * @return 位置
     * @since 3.2.1
     */
    public static int lastIndexOf(final CharSequence str, final CharSequence searchStr, int fromIndex, int toIndex, boolean ignoreCase) {
        //需要调整
        if (isEmpty(str) || isEmpty(searchStr)) {
            return -1;
        }
        String stringScope = sub(str, fromIndex, toIndex);
        int beforeIndex = fromIndex < 0 ? (Math.max(str.length() + fromIndex, 0)) : fromIndex;
        int endIndex = toIndex <= 0 ? (Math.max(str.length() + toIndex, 0)) : toIndex;
        if (stringScope.length() < searchStr.length()) {
            return -1;
        }
        if (!ignoreCase) {
            // 不忽略大小写调用JDK方法
            int i = str.toString().lastIndexOf(searchStr.toString(), beforeIndex);
            if (i < endIndex) {
                return i;
            }
            return -1;

        }
        for (int i = stringScope.length()-searchStr.length(); i >= 0; i--) {
            if (isSubEquals(stringScope, i, searchStr, 0, searchStr.length(), true)) {
                return i + beforeIndex;
            }
        }
        return -1;
    }

    /**
     * 截取两个字符串的不同部分（长度一致），判断截取的子串是否相同<br>
     * 任意一个字符串为<code>null</code>返回false
     *
     * @param str1       第一个字符串
     * @param start1     第一个字符串开始的位置
     * @param str2       第二个字符串
     * @param start2     第二个字符串开始的位置
     * @param length     截取长度
     * @param ignoreCase 是否忽略大小写
     * @return 子串是否相同
     */
    public static boolean isSubEquals(CharSequence str1, int start1, CharSequence str2, int start2, int length, boolean ignoreCase) {
        if (null == str1 || null == str2) {
            return false;
        }
        return str1.toString().regionMatches(ignoreCase, start1, str2.toString(), start2, length);
    }

    /**
     * 把<code>String</code>转换成指定类型
     *
     * @param string 需要转换的string
     * @param cl     指定的类型
     * @return 返回对象为<code>Object</code>类型
     */
    public static Object changeType(String string, Class cl) {
        if (isEmpty(string)) {
            return null;
        }
        if (string.getClass().equals(cl)) {
            return string;
        }
        if (cl == Integer.class || cl == int.class) {
            return Integer.valueOf(string);
        }
        if (cl == Float.class || cl == float.class) {
            return Float.valueOf(string);
        }
        if (cl == Short.class || cl == short.class) {
            return Short.valueOf(string);
        }
        if (cl == Long.class || cl == long.class) {
            return Long.valueOf(string);
        }
        if (cl == Double.class || cl == double.class) {
            return Double.valueOf(string);
        }
        if (cl == Byte.class || cl == byte.class) {
            return Byte.valueOf(string);
        }
        if (cl == Boolean.class || cl == boolean.class) {
            return Boolean.valueOf(string);
        }
        if (cl == Character.class || cl == char.class) {
            return (string.charAt(0));
        }
        return JsonUtils.json2Obj(string, cl);
    }

    /**
     * 把<code>String</code>转换成指定类型
     *
     * @param string 需要转换的string
     * @param cl     指定的类型
     * @return 返回对象为指定类型<code>T</code>
     */
    public static <T> T changeTypeWithType(String string, Class<T> cl) {
        if (isEmpty(string)) {
            return null;
        }
        if (string.getClass().equals(cl)) {
            return (T) string;
        }
        if (cl == Integer.class || cl == int.class) {
            return (T) Integer.valueOf(string);
        }
        if (cl == Float.class || cl == float.class) {
            return (T) Float.valueOf(string);
        }
        if (cl == Short.class || cl == short.class) {
            return (T) Short.valueOf(string);
        }
        if (cl == Long.class || cl == long.class) {
            return (T) Long.valueOf(string);
        }
        if (cl == Double.class || cl == double.class) {
            return (T) Double.valueOf(string);
        }
        if (cl == Byte.class || cl == byte.class) {
            return (T) Byte.valueOf(string);
        }
        if (cl == Boolean.class || cl == boolean.class) {
            return (T) Boolean.valueOf(string);
        }
        if (cl == Character.class || cl == char.class) {
            return (T) ((Character) string.charAt(0));
        }
        return JsonUtils.json2Obj(string, cl);
    }

    /**
     * 是否以某字符串开头
     * @param string 字符串
     * @param searchStr 头部字符串
     * @return 是否以某字符串开头
     */
    public static boolean startWith(CharSequence string, CharSequence searchStr) {
        if (isNotEmpty(string) && isNotEmpty(searchStr) && string.length() >= searchStr.length()) {
            return searchStr.equals(sub(string, 0, searchStr.length()));
        } else {
            return string == searchStr;
        }
    }
    /**
     * 是否以某字符串结尾
     * @param string 字符串
     * @param searchStr 结尾字符串
     * @return 是否以某字符串结尾
     */
    public static boolean endWith(CharSequence string, CharSequence searchStr) {
        if (isNotEmpty(string) && isNotEmpty(searchStr) && string.length() >= searchStr.length()) {
            return searchStr.equals(sub(string, -1*searchStr.length(), 0));
        } else {
            return string == searchStr;
        }
    }

    /**
     * 清除后缀
     * @param string 原字符串
     * @param suf 需要清除的后缀
     * @return 清除后字符串
     */
    public static String removeSuf(CharSequence string, CharSequence suf){
        return endWith(string, suf)? sub(string, 0, string.length()-suf.length()):string.toString();
    }

    /**
     *清除前缀
     * @param string 原字符串
     * @param pre 需要清除前缀
     * @return 清除后字符串
     */
    public static String removePre(CharSequence string, CharSequence pre){
        return startWith(string, pre)? sub(string, pre.length(), string.length()):string.toString();
    }

    /**
     *清除最后一位查到的后 字符串
     * @param string 原字符串
     * @param searchStr 查找字符串
     * @return 清除后字符串
     */
    public static String removeAfterLast(CharSequence string, CharSequence searchStr){
        return string.toString().substring(0, string.toString().lastIndexOf(searchStr.toString()));
    }

    /**
     *清除第一位查到的前 字符串
     * @param string 原字符串
     * @param searchStr 查找字符串
     * @return 清除后字符串
     */
    public static String removeBeforeFirst(CharSequence string, CharSequence searchStr){
        return string.toString().substring(string.toString().indexOf(searchStr.toString())+1, string.length());
    }

    /**
     * 异常栈转字符串
     * @param e 异常信息
     * @return 转换后的字符串
     */
    public static String getErrorInfoFromException(Throwable e) {
        try (StringWriter sw = new StringWriter();
             PrintWriter pw = new PrintWriter(sw)){
            e.printStackTrace(pw);
            return  sw.toString();
        } catch (Exception e2) {
            return null;
        }
    }

    /**
     * 检查字符序列是否只包含数字
     *
     * <pre>
     * StringUtils.isNumeric(null)   = false
     * StringUtils.isNumeric("")     = false
     * StringUtils.isNumeric("  ")   = false
     * StringUtils.isNumeric("123")  = true
     * StringUtils.isNumeric("\u0967\u0968\u0969")  = true
     * StringUtils.isNumeric("12 3") = false
     * StringUtils.isNumeric("ab2c") = false
     * StringUtils.isNumeric("12-3") = false
     * StringUtils.isNumeric("12.3") = false
     * StringUtils.isNumeric("-123") = false
     * StringUtils.isNumeric("+123") = false
     * </pre>
     *
     * @param cs  要检查的字符序列
     * @return 只包含数字则为true，否则为false
     */
    public static boolean isNumeric(final CharSequence cs) {
        if (cs == null || cs.length() == 0) {
            return false;
        }
        final int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (!Character.isDigit(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }


}
